package com.helin.helinhealth.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.helin.helinhealth.common.Result;
import com.helin.helinhealth.service.AuthService;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

/**
 * JWT认证过滤器
 * 
 * <p>
 * 该过滤器用于拦截HTTP请求，验证JWT令牌的有效性。
 * 实现了OncePerRequestFilter，确保每个请求只会执行一次过滤。
 * </p>
 * 
 * <p>
 * 过滤器会检查请求头中是否包含有效的JWT令牌，如果令牌有效，则允许请求继续执行；
 * 如果令牌无效或不存在，则根据请求路径判断是否需要身份验证，
 * 对于需要身份验证的路径，会拒绝请求并返回401错误。
 * </p>
 */
@Component
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    /**
     * 认证服务
     */
    @Autowired
    private AuthService authService;
    
    /**
     * JSON转换器
     */
    @Autowired
    private ObjectMapper objectMapper;
    
    /**
     * 令牌请求头名称
     */
    @Value("${jwt.header-name:Authorization}")
    private String headerName;
    
    /**
     * 令牌前缀
     */
    @Value("${jwt.token-prefix:Bearer }")
    private String tokenPrefix;
    
    /**
     * 开发环境标志
     */
    @Value("${spring.profiles.active:}")
    private String activeProfile;
    
    /**
     * 无需认证的路径列表
     */
    private List<String> permitUrls = Arrays.asList(
        "/api/auth/**",           // 认证接口
        "/v3/api-docs/**",        // Swagger文档
        "/swagger-ui/**",         // Swagger UI
        "/swagger-resources/**",  // Swagger资源
        "/webjars/**",            // Swagger依赖
        "/doc.html",              // Knife4j文档
        "/favicon.ico"            // 网站图标
    );
    
    /**
     * 路径匹配器
     */
    private AntPathMatcher pathMatcher = new AntPathMatcher();

    /**
     * 过滤器逻辑实现
     * 
     * @param request HTTP请求
     * @param response HTTP响应
     * @param filterChain 过滤器链
     * @throws ServletException Servlet异常
     * @throws IOException IO异常
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        // 开发环境下不进行JWT认证，方便测试
        if (activeProfile.contains("dev")) {
            filterChain.doFilter(request, response);
            return;
        }
        
        // 检查当前请求是否需要认证
        String requestURI = request.getRequestURI();
        if (isPermitUrl(requestURI)) {
            filterChain.doFilter(request, response);
            return;
        }
        
        // 获取请求头中的令牌
        String header = request.getHeader(headerName);
        if (header == null || !header.startsWith(tokenPrefix)) {
            // 令牌不存在，返回401未授权错误
            handleAuthenticationFailure(response);
            return;
        }
        
        // 提取令牌并验证
        String token = header.substring(tokenPrefix.length());
        if (!authService.validateToken(token)) {
            // 令牌无效，返回401未授权错误
            handleAuthenticationFailure(response);
            return;
        }
        
        // 令牌有效，继续执行过滤器链
        filterChain.doFilter(request, response);
    }
    
    /**
     * 判断请求路径是否在无需认证的列表中
     * 
     * @param requestURI 请求路径
     * @return 是否无需认证
     */
    private boolean isPermitUrl(String requestURI) {
        for (String permitUrl : permitUrls) {
            if (pathMatcher.match(permitUrl, requestURI)) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * 处理认证失败的情况
     * 
     * @param response HTTP响应
     * @throws IOException IO异常
     */
    private void handleAuthenticationFailure(HttpServletResponse response) throws IOException {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType("application/json;charset=UTF-8");
        
        Result result = Result.custom(401, "未授权（Token失效）", null, false, "/api");
        response.getWriter().write(objectMapper.writeValueAsString(result));
    }
} 